﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using TouchSocket.Core;
using TouchSocket.Sockets;
using YS.Domain.IoT.Extension;
using YS.Domain.IoT.Receive;
using YS.Domain.IoT;
using NLog;
using YS.Domain.IoT.Logging;
using YS.Domain.IoT.Logging.Dto;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;
using Yitter.IdGenerator;
using Microsoft.Extensions.Logging;
using YS.K12.Application.IoT.RedisHub;
namespace YS.K12.Application.IoT.Service.ReplyCommand
{
    public class ReplyCommandService: IReplyCommandService
    {
        private readonly ITcpService _tcpService;
        private readonly IHttp _http;
        private readonly JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions() { Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
        private readonly NLog.ILogger _commandlogger =LogManager.GetLogger("CommandContext");
        public readonly ILogger<ReplyCommandService> _replyLogger;
        private readonly RedisHubLifetimeManager _redisHub;
        public ReplyCommandService(
            ITcpService tcpService,
            IHttp http,
            ILogger<ReplyCommandService> replyLogger,
            RedisHubLifetimeManager redisHub)
        {
            _tcpService = tcpService;
            _http = http;
            _replyLogger= replyLogger;
            _redisHub= redisHub;
        }
        /// <summary>
        /// To: 可以做收到与回复的日志写入
        /// 通用回复函数
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="response">返回信息</param>
        /// <param name="request">请求信息</param>
        /// <param name="newPrvkey">1000协议才传入</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public async Task<bool> SendAsync<T>(T response,RequestInfo request,string newPrvkey=null) where T : BaseCmd
        {
            var log = new CommandContext(request);
            try
            {
                var body = JsonSerializer.Serialize(response, jsonSerializerOptions);
                var prvkey =await _http.GetPrivKey(request.Mid);
                //设置key
                var key = EcodeKey(request.Key, request.Version, request.Mid, request.MessageId);
                byte[] newbody = [];
                if (request.Key == "1000")//初始化的时候特殊处理
                {
                    ///设置签名
                    newbody = Encoding.GetEncoding("GB2312").GetBytes(CommandExtension.GetAgreement(body, key, newPrvkey, response.Mid, response.TimeSp.ToString()));
                    await BroadcastSendAsync(request.ClientId, newbody);//没有登录只能客户端Id回复
                }
                else
                {
                    if (request.Mid != response.Mid)
                    {
                        //请求的设备号不等于返回的设备号
                        throw new Exception("Mid不匹配");
                    }
                    ///设置签名
                    newbody = Encoding.UTF8.GetBytes(CommandExtension.GetAgreement(body, key, prvkey.PrivaKey, request.Mid, response.TimeSp.ToString()));

                    await BroadcastSendAsync(request.Mid, newbody);//登录成功用设备号回复
                }
                //log.Id = DateTime.Now.Ticks.ToString();
                log.ResponseId = DateTime.Now.Ticks.ToString();
                log.ResponseBody = body;
            }
            catch (Exception ex)
            {
                log.Exception = ex.Message;
            }
            log.EndTime = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
            _commandlogger.Warn("{JoviCommandContext}", log);
            return true;
        }
        public async Task<bool> SendAsync<T>(T response, string command, string messageId=null) where T : BaseCmd
        {
            try
            {
                var body = JsonSerializer.Serialize(response, jsonSerializerOptions);
                var prvkey =await _http.GetPrivKey(response.Mid);
                //设置key
                var key = EcodeKey(command, "1", response.Mid, messageId ?? YitIdHelper.NextId().ToString());
                var nowBody = CommandExtension.GetAgreement(body, key, prvkey.PrivaKey, response.Mid, response.TimeSp.ToString());
                await BroadcastSendAsync(response.Mid, Encoding.UTF8.GetBytes(nowBody));
            }
            catch (Exception ex)
            {
                _replyLogger.LogError(ex, $"发送出错:设备编号:{ response.Mid}|指令:{command}");
               return false;
            }
             return true;
        }
        private string EcodeKey(string key, string version, string mid, string messageId)
        {

            if (string.IsNullOrEmpty(version))
            {
                return key;
            }
            else
            {
                return $"{key}-{mid}-{messageId}-{1}";
            }
        }

        public async Task<bool> NoSendAsync(RequestInfo request)
        {
            var log = new CommandContext(request);
            try
            {
                log.ResponseId = DateTime.Now.Ticks.ToString();
                log.ResponseBody = "0";
            }
            catch (Exception ex)
            {
                log.Exception = ex.Message;
            }
            log.EndTime = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
            _commandlogger.Warn("{JoviCommandContext}", log);
            return await Task.FromResult(true);
        }
        /// <summary>
        ///广播推送
        /// </summary>
        /// <param name="id"></param>
        /// <param name="memory"></param>
        /// <returns></returns>
        private async Task BroadcastSendAsync(string id, ReadOnlyMemory<byte> memory) {

            //await _redisHub.PublishAsync("2411300002", memory);

            //本地找不到客户端,发布广播
            if (!_tcpService.TryGetClient(id, out _))
            {
                await _redisHub.PublishAsync(id, memory);
            }
            else
            {
                //找到了直接发
                await _tcpService.SendAsync(id, memory);
            }
        }
    }
}
